﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using Httlgd.DataCenter.Services.Sms.BusinessComponent;
using System.Collections.Specialized;
using System.Collections;
using Httlgd.DataCenter.Services.Core;

namespace Httlgd.DataCenter.Services.Sms.ServiceLibrary
{
    public class FileTransferService : IFileTransferService
    {
        public DownloadFileResponse DownloadFile(DownloadFileRequest request)
        {
            //Validate request
            string paramName = "", error = "";
            if (!this.ValidateDownloadRequest(request,out paramName, out error))
            {
                throw new ArgumentException(error, paramName);
            }

            DownloadFileResponse downloadFI = new DownloadFileResponse();
            downloadFI.RelatedRecordID = request.RelatedRecordID;

            if (request.DocumentType == DocumentType.SermonAudio)
            {
                SermonBC sermonBC = new SermonBC();

                string filename = "";
                string sermonAudioFilePath = sermonBC.GetAudioFilePath(request.RelatedRecordID, out filename);

                if (String.IsNullOrEmpty(sermonAudioFilePath))
                {
                    downloadFI.Error = "Requested sermon is not found.";
                    return downloadFI;
                }


                //Check the local file
                System.IO.FileInfo fileInfo = new System.IO.FileInfo(sermonAudioFilePath);

                if (!fileInfo.Exists)
                {
                    downloadFI.Error = sermonAudioFilePath + " is not found.";
                    return downloadFI;
                }

                //Read the file to stream
                System.IO.FileStream stream = new System.IO.FileStream(sermonAudioFilePath, System.IO.FileMode.Open, System.IO.FileAccess.Read);

                // return result
                downloadFI.FileName = filename;
                downloadFI.Length = fileInfo.Length;
                downloadFI.FileByteStream = stream;
            }
            else if (request.DocumentType == DocumentType.Attachment)
            {
                AttachmentBC attBC = new AttachmentBC();

                string filename = "";
                byte[] attData = attBC.GetAttachmentBinaryData(request.RelatedRecordID, out filename);

                if (attData == null || String.IsNullOrEmpty(filename))
                {
                    downloadFI.Error = "Requested attachment is not found.";
                    return downloadFI;
                }


                //Read byte array to the stream.
                MemoryStream memStream = new MemoryStream();

                int offset = 0;
                int chunkSize = 2048;
                while (offset < attData.Length)
                {
                    if (attData.Length - offset < chunkSize)
                        chunkSize = attData.Length - offset;

                    memStream.Write(attData, offset, chunkSize);
                    offset += chunkSize;
                }

                memStream.Position = 0;

                downloadFI.FileName = filename;
                downloadFI.Length = attData.Length;
                downloadFI.FileByteStream = memStream;
            }

            return downloadFI;
        }

        public void UploadFile(UploadFileRequest request)
        {
            //Validate request
            string error = "", paramName="";            
            if (!ValidateUploadRequest(request, out paramName, out error))
            {
                throw new ArgumentException(error, paramName);
            }


            //Sermon audio file -> save the audio file to local folder.
            if (request.DocumentType == DocumentType.SermonAudio)
            {
                SermonBC sermonBC = new SermonBC();
                if (sermonBC.IsExisted(new DTOKey(request.RelatedRecordID)))
                {
                    //create output folder, if does not exists
                    if (!System.IO.Directory.Exists(Parameters.AudioFileFolder)) System.IO.Directory.CreateDirectory(Parameters.AudioFileFolder);

                    // kill target file, if already exists
                    string filePath = System.IO.Path.Combine(Parameters.AudioFileFolder, request.FileName);
                    if (System.IO.File.Exists(filePath)) System.IO.File.Delete(filePath);

                    int chunkSize = 2048;
                    byte[] buffer = new byte[chunkSize];

                    using (System.IO.FileStream writeStream = new System.IO.FileStream(filePath, System.IO.FileMode.CreateNew, System.IO.FileAccess.Write))
                    {
                        do
                        {
                            // read bytes from input stream
                            int bytesRead = request.FileByteStream.Read(buffer, 0, chunkSize);
                            if (bytesRead == 0) break;

                            // write bytes to output stream
                            writeStream.Write(buffer, 0, bytesRead);
                        } while (true);
                        

                        writeStream.Close();
                    }

                    try
                    {
                        //Update file path for related sermon record.
                        sermonBC.UpdateSermonAudioFilePath(request.RelatedRecordID, request.FileName, filePath);
                    }
                    catch (Exception ex)
                    {
                        File.Delete(filePath);
                    }
                }
                else
                {
                    throw new Exception(String.Format("Sermon with ID {0} does not existed.", request.RelatedRecordID));
                }
            }
            else if (request.DocumentType == DocumentType.Attachment) //Attachment file -> insert binary data to associated DB record.
            {
                AttachmentBC attBC = new AttachmentBC();
                if (attBC.IsExisted(new DTOKey(request.RelatedRecordID)))
                {
                    int chunkSize = 2048; //2KB
                    byte[] data = new byte[request.Length];

                    // Now read s into a data buffer.                
                    int numBytesToRead = (int)request.Length;
                    int numBytesRead = 0;
                    while (numBytesToRead > 0)
                    {
                        if (numBytesToRead < chunkSize)
                            chunkSize = numBytesToRead;

                        // Read may return anything from 0 to Chunksize.
                        int n = request.FileByteStream.Read(data, numBytesRead, chunkSize);
                        // The end of the file is reached.
                        if (n == 0)
                        {
                            break;
                        }
                        numBytesRead += n;
                        numBytesToRead -= n;
                    }

                    //update data to associated attachment record.
                    attBC.UpdateAttachmentBinaryData(request.RelatedRecordID, data);
                }
                else
                {
                    throw new Exception(String.Format("Attachment with ID {0} does not existed.", request.RelatedRecordID));
                }
            }
        }

        private bool ValidateUploadRequest(UploadFileRequest request, out string paramName, out string error)
        {
            error = "";
            paramName = "";

            if (request.RelatedRecordID <= 0)
            {
                error = "ID number of related record is not provided.";
                paramName = "RelatedRecordID";
                return false;
            }

            if (request.Length <= 0)
            {
                error = "Length of uploaded stream is not provided.";
                paramName = "Length";
                return false;
            }

            return string.IsNullOrEmpty(error);
        }

        private bool ValidateDownloadRequest(DownloadFileRequest request, out string paramName, out string error)
        {
            error = "";
            paramName = "";

            if (request.RelatedRecordID <= 0)
            {
                error = "ID number of related record is not provided.";
                paramName = "RelatedRecordID";
            }

            return string.IsNullOrEmpty(error);
        }
    }
}
